#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

// 获取虚拟地址对应的物理地址（需root权限）
unsigned long virt_to_phys(pid_t pid, unsigned long addr) {
    char pagemap_path[64];
    snprintf(pagemap_path, sizeof(pagemap_path), "/proc/%d/pagemap", pid);

    int fd = open(pagemap_path, O_RDONLY);
    if (fd < 0) {
        perror("open pagemap file failed");
        return 0;
    }

    // 计算 pagemap中的偏移量（每个虚拟页占8字节）
    unsigned long offset = (addr / sysconf(_SC_PAGESIZE)) * sizeof(uint64_t);

    if (lseek(fd, offset, SEEK_SET) < 0) {
        perror("lseek failed");
        close(fd);
        return 0;
    }

    uint64_t entry;
    if (read(fd, &entry, sizeof(entry)) != sizeof(entry)) {
        perror("read pagemap entry failed");
        close(fd);
        return 0;
    }
    
    close(fd);

    //检查页面是否在内存中

    if(!(entry & (1UL << 63))) {
        fprintf(stderr, "page not in memory\n");
        return 0;
    }

    // 物理页帧号PFN是低55位
    unsigned long pfn = entry & ((1UL << 55) - 1);
    return pfn * sysconf(_SC_PAGESIZE) + (addr % sysconf(_SC_PAGESIZE));
}

int main(int argc, char *argv[]) {
    uint8_t *ptr = (void*)0xfffefbe01000; //修改Proc_HVA
    unsigned long phys_addr = virt_to_phys(139166, (unsigned long)ptr); //修改虚机进程号
    printf("Virtual address: %lx\n", (unsigned long)ptr);
    printf("Physical address: %lx\n", phys_addr);
    return 0;
}